// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <cstring>
#include <time.h>
#include <chrono>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/ioctl.h>

#include "SocketBase.h"
#include "securec.h"
#include "RrConfig.h"

namespace master {

BaseSocket::BaseSocket()
{
    fd = ERROR_FD;
    port = 0;
}

BaseSocket::~BaseSocket()
{
    if (fd <= 0) {
        close(fd);
        fd = ERROR_FD;
    }
}

void BaseSocket::socketSetParamInfo(uint16_t ipPort)
{
    port = ipPort;
}

int BaseSocket::socketSetFd(void)
{
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        LOG_ERROR_FMT("Socket SOCK_STREAM failed fd %d", fd);
        return ENC_FAIL;
    }
    return ENC_OK;
}

int BaseSocket::setReUseAddr(void)
{
    int reuse = 1;

    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0) {
        LOG_ERROR_FMT("Set sock opt of SO_REUSEADDR fail iFd %d", fd);
        return ENC_FAIL;
    }

    return ENC_OK;
}

int BaseSocket::setBindDevice(void)
{
    struct ifreq dev0;
    if (memset_s(&dev0, sizeof(dev0), 0, sizeof(dev0)) < 0) {
        LOG_ERROR_FMT("Memset_s failed");
        return ENC_FAIL;
    }
    snprintf(dev0.ifr_name, sizeof(dev0.ifr_name), ETH0);
    ioctl(fd, SIOCGIFINDEX, &dev0);
    if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *) &dev0, sizeof(dev0)) < 0) {
        LOG_ERROR_FMT("Set sock opt of SO_REUSEADDR failed");
        return ENC_FAIL;
    }

    return ENC_OK;
}

int BaseSocket::setTimeOut(void)
{
    struct timeval tv_timeout;
    tv_timeout.tv_sec = 2;
    tv_timeout.tv_usec = 0;

    if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv_timeout, sizeof(timeval)) < 0) {
        LOG_ERROR_FMT("Set sock opt of SO_RCVTIMEO fail iFd %d", fd);
        return ENC_FAIL;
    }

    if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *) &tv_timeout, sizeof(timeval)) < 0) {
        LOG_ERROR_FMT("Set sock opt of SO_RCVTIMEO failed iFd %d", fd);
        return ENC_FAIL;
    }
    return ENC_OK;
}

int BaseSocket::socketSetSockOpt(void)
{
    if (ENC_OK != this->setReUseAddr()) {
        LOG_ERROR_FMT("Set socket port addr reuse fail");
        return ENC_FAIL;
    }

    if (ENC_OK != this->setBindDevice()) {
        LOG_ERROR_FMT("Set socket bind eth0 fail");
        return ENC_FAIL;
    }

    return ENC_OK;
}

int BaseSocket::socketBind(void)
{
    int ret = ENC_OK;
    struct sockaddr_in addr;

    ret = memset_s(&addr, sizeof(sockaddr_in) + sizeof(int), 0, sizeof(sockaddr_in));
    if (ret < 0) {
        LOG_ERROR_FMT("Socket bind memset_s fail ret %d", ret);
        return ENC_FAIL;
    }

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(rr::gMasterConfig.iMaster.privateIp.c_str());

    ret = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
    if (ret < 0) {
        LOG_ERROR_FMT("Socket bind fail ret %d", ret);
        return ENC_FAIL;
    }
    return ENC_OK;
}

int BaseSocket::socketListen(void)
{
    int ret = ENC_OK;
    int backlog = MAX_LISTEN_NUM;

    ret = listen(fd, backlog);
    if (ret < 0) {
        LOG_ERROR_FMT("Socket listen fail ret %d", ret);
        close(fd);
        return ENC_FAIL;
    }

    return ENC_OK;
}

int BaseSocket::setFdUnblock(int connFd)
{
    int flags = 0;
    flags = fcntl(connFd, F_GETFL, 0);
    if (flags < 0) {
        LOG_ERROR_FMT("F_GETFL failed");
        return ENC_FAIL;
    }

    if (fcntl(connFd, F_SETFL, flags | O_NONBLOCK) < 0) {
        LOG_ERROR_FMT("F_SETFL failed");
        return ENC_FAIL;
    }

    return ENC_OK;
}

int BaseSocket::socketAccept(char *ipAddr, uint16_t *ipPort)
{
    int ret = ENC_OK;
    int clientFd = 0;
    uint16_t clientPort = 0;
    char addrBuff[INET_ADDRSTRLEN + 1] = {0};
    struct sockaddr_in clientAddr;
    socklen_t len = sizeof(clientAddr);

    clientFd = accept(fd, (sockaddr *) &clientAddr, &len);
    if (clientFd < 0) {
        LOG_ERROR_FMT("Socket accept fail clientFd %d", clientFd);
        return ENC_FAIL;
    }

    this->setFdUnblock(clientFd);

    inet_ntop(AF_INET, &clientAddr.sin_addr, addrBuff, INET_ADDRSTRLEN);
    clientPort = ntohs(clientAddr.sin_port);

    ret = memcpy_s(ipAddr, INET_ADDRSTRLEN, addrBuff, INET_ADDRSTRLEN);
    if (ret < 0) {
        LOG_ERROR_FMT("Memcpy_s failed");
        return ENC_FAIL;
    }
    *ipPort = clientPort;

    return clientFd;

}

int BaseSocket::socketRecv(int clientFd, uint8_t *recvBuf, uint32_t recvBufLen, int *readLen)
{
    if (isClose(clientFd)) {
        LOG_ERROR_FMT("Client fd %d error", clientFd);
        return ENC_FAIL;
    }
    
    int recvLen = recv(clientFd, recvBuf, recvBufLen, 0);
    if (recvLen < 0) {
        if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN){
            LOG_ERROR_FMT("Recv error clientFd = %d errno = %d", clientFd, errno);
            *readLen = 0;
            this->eagainCnt++;
            if(eagainCnt > EAGAIN_TIME_OUT){
                return ENC_FAIL;
            }
            usleep(500);
            return ENC_OK;
        } else if (errno == ECONNRESET){
            LOG_INFO_FMT("Connection reset by peer clientFd = %d errno = %d", clientFd, errno);
            return ENC_FAIL;
        } else {
            LOG_ERROR_FMT("ClientFd = %d errno == %d error", clientFd, errno);
            return ENC_FAIL;
        }
    } else if (recvLen == 0) {
        return ENC_FAIL;
    }

    this->eagainCnt = 0;
    *readLen = recvLen;

    return ENC_OK;
}

int BaseSocket::socketSend(int clientFd, uint8_t *sendBuf, uint32_t sendLen, uint32_t *sendRetLen)
{
    if (isClose(clientFd)) {
        LOG_ERROR_FMT("Client fd %d error", clientFd);
        return ENC_FAIL;
    }

    uint32_t sendOffset = 0;
    int retLen = send(clientFd, sendBuf + sendOffset, sendLen - sendOffset, MSG_NOSIGNAL);
    if (retLen < 0) {
        LOG_DEBUG_FMT("Socket send fail sendRetLen %d", retLen);
        if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN){
            LOG_DEBUG_FMT("Socket send fail errno %d", errno);
            usleep(500);
            return ENC_OK;
        }

        LOG_ERROR_FMT("Socket send fail errno %d", errno);
        return ENC_FAIL;
    }
    *sendRetLen = retLen;

    return ENC_OK;
}

void BaseSocket::socketClose(void)
{
    if (fd > 0) {
        close(fd);
        fd = ERROR_FD;
    }
}

void BaseSocket::socketClose(int sockFd)
{
    if (sockFd > 0) {
        close(sockFd);
        sockFd = ERROR_FD;
    }
}

bool BaseSocket::isClose(const int iFd)
{
    return iFd < 0;
}
};